home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2000 March / maximum-cd-2000-03.iso / Quake3 Game Source / Q3AGameSource.exe / Main / cg_players.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-01-18  |  39.0 KB  |  1,546 lines

  1. // Copyright (C) 1999-2000 Id Software, Inc.
  2. //
  3. // cg_players.c -- handle the media and animation for player entities
  4. #include "cg_local.h"
  5.  
  6. char    *cg_customSoundNames[MAX_CUSTOM_SOUNDS] = {
  7.     "*death1.wav",
  8.     "*death2.wav",
  9.     "*death3.wav",
  10.     "*jump1.wav",
  11.     "*pain25_1.wav",
  12.     "*pain50_1.wav",
  13.     "*pain75_1.wav",
  14.     "*pain100_1.wav",
  15.     "*falling1.wav",
  16.     "*gasp.wav",
  17.     "*drown.wav",
  18.     "*fall1.wav",
  19.     "*taunt.wav"
  20. };
  21.  
  22.  
  23. /*
  24. ================
  25. CG_CustomSound
  26.  
  27. ================
  28. */
  29. sfxHandle_t    CG_CustomSound( int clientNum, const char *soundName ) {
  30.     clientInfo_t *ci;
  31.     int            i;
  32.  
  33.     if ( soundName[0] != '*' ) {
  34.         return trap_S_RegisterSound( soundName );
  35.     }
  36.  
  37.     if ( clientNum < 0 || clientNum >= MAX_CLIENTS ) {
  38.         clientNum = 0;
  39.     }
  40.     ci = &cgs.clientinfo[ clientNum ];
  41.  
  42.     for ( i = 0 ; i < MAX_CUSTOM_SOUNDS && cg_customSoundNames[i] ; i++ ) {
  43.         if ( !strcmp( soundName, cg_customSoundNames[i] ) ) {
  44.             return ci->sounds[i];
  45.         }
  46.     }
  47.  
  48.     CG_Error( "Unknown custom sound: %s", soundName );
  49.     return 0;
  50. }
  51.  
  52.  
  53.  
  54. /*
  55. =============================================================================
  56.  
  57. CLIENT INFO
  58.  
  59. =============================================================================
  60. */
  61.  
  62. /*
  63. ======================
  64. CG_ParseAnimationFile
  65.  
  66. Read a configuration file containing animation coutns and rates
  67. models/players/visor/animation.cfg, etc
  68. ======================
  69. */
  70. static qboolean    CG_ParseAnimationFile( const char *filename, clientInfo_t *ci ) {
  71.     char        *text_p, *prev;
  72.     int            len;
  73.     int            i;
  74.     char        *token;
  75.     float        fps;
  76.     int            skip;
  77.     char        text[20000];
  78.     fileHandle_t    f;
  79.     animation_t *animations;
  80.  
  81.     animations = ci->animations;
  82.  
  83.     // load the file
  84.     len = trap_FS_FOpenFile( filename, &f, FS_READ );
  85.     if ( len <= 0 ) {
  86.         return qfalse;
  87.     }
  88.     if ( len >= sizeof( text ) - 1 ) {
  89.         CG_Printf( "File %s too long\n", filename );
  90.         return qfalse;
  91.     }
  92.     trap_FS_Read( text, len, f );
  93.     text[len] = 0;
  94.     trap_FS_FCloseFile( f );
  95.  
  96.     // parse the text
  97.     text_p = text;
  98.     skip = 0;    // quite the compiler warning
  99.  
  100.     ci->footsteps = FOOTSTEP_NORMAL;
  101.     VectorClear( ci->headOffset );
  102.     ci->gender = GENDER_MALE;
  103.  
  104.     // read optional parameters
  105.     while ( 1 ) {
  106.         prev = text_p;    // so we can unget
  107.         token = COM_Parse( &text_p );
  108.         if ( !token ) {
  109.             break;
  110.         }
  111.         if ( !Q_stricmp( token, "footsteps" ) ) {
  112.             token = COM_Parse( &text_p );
  113.             if ( !token ) {
  114.                 break;
  115.             }
  116.             if ( !Q_stricmp( token, "default" ) || !Q_stricmp( token, "normal" ) ) {
  117.                 ci->footsteps = FOOTSTEP_NORMAL;
  118.             } else if ( !Q_stricmp( token, "boot" ) ) {
  119.                 ci->footsteps = FOOTSTEP_BOOT;
  120.             } else if ( !Q_stricmp( token, "flesh" ) ) {
  121.                 ci->footsteps = FOOTSTEP_FLESH;
  122.             } else if ( !Q_stricmp( token, "mech" ) ) {
  123.                 ci->footsteps = FOOTSTEP_MECH;
  124.             } else if ( !Q_stricmp( token, "energy" ) ) {
  125.                 ci->footsteps = FOOTSTEP_ENERGY;
  126.             } else {
  127.                 CG_Printf( "Bad footsteps parm in %s: %s\n", filename, token );
  128.             }
  129.             continue;
  130.         } else if ( !Q_stricmp( token, "headoffset" ) ) {
  131.             for ( i = 0 ; i < 3 ; i++ ) {
  132.                 token = COM_Parse( &text_p );
  133.                 if ( !token ) {
  134.                     break;
  135.                 }
  136.                 ci->headOffset[i] = atof( token );
  137.             }
  138.             continue;
  139.         } else if ( !Q_stricmp( token, "sex" ) ) {
  140.             token = COM_Parse( &text_p );
  141.             if ( !token ) {
  142.                 break;
  143.             }
  144.             if ( token[0] == 'f' || token[0] == 'F' ) {
  145.                 ci->gender = GENDER_FEMALE;
  146.             } else if ( token[0] == 'n' || token[0] == 'N' ) {
  147.                 ci->gender = GENDER_NEUTER;
  148.             } else {
  149.                 ci->gender = GENDER_MALE;
  150.             }
  151.             continue;
  152.         }
  153.  
  154.         // if it is a number, start parsing animations
  155.         if ( token[0] >= '0' && token[0] <= '9' ) {
  156.             text_p = prev;    // unget the token
  157.             break;
  158.         }
  159.         Com_Printf( "unknown token '%s' is %s\n", token, filename );
  160.     }
  161.  
  162.     // read information for each frame
  163.     for ( i = 0 ; i < MAX_ANIMATIONS ; i++ ) {
  164.  
  165.         token = COM_Parse( &text_p );
  166.         if ( !token ) {
  167.             break;
  168.         }
  169.         animations[i].firstFrame = atoi( token );
  170.         // leg only frames are adjusted to not count the upper body only frames
  171.         if ( i == LEGS_WALKCR ) {
  172.             skip = animations[LEGS_WALKCR].firstFrame - animations[TORSO_GESTURE].firstFrame;
  173.         }
  174.         if ( i >= LEGS_WALKCR ) {
  175.             animations[i].firstFrame -= skip;
  176.         }
  177.  
  178.         token = COM_Parse( &text_p );
  179.         if ( !token ) {
  180.             break;
  181.         }
  182.         animations[i].numFrames = atoi( token );
  183.  
  184.         token = COM_Parse( &text_p );
  185.         if ( !token ) {
  186.             break;
  187.         }
  188.         animations[i].loopFrames = atoi( token );
  189.  
  190.         token = COM_Parse( &text_p );
  191.         if ( !token ) {
  192.             break;
  193.         }
  194.         fps = atof( token );
  195.         if ( fps == 0 ) {
  196.             fps = 1;
  197.         }
  198.         animations[i].frameLerp = 1000 / fps;
  199.         animations[i].initialLerp = 1000 / fps;
  200.     }
  201.  
  202.     if ( i != MAX_ANIMATIONS ) {
  203.         CG_Printf( "Error parsing animation file: %s", filename );
  204.         return qfalse;
  205.     }
  206.  
  207.     return qtrue;
  208. }
  209.  
  210. /*
  211. ==========================
  212. CG_RegisterClientSkin
  213. ==========================
  214. */
  215. static qboolean    CG_RegisterClientSkin( clientInfo_t *ci, const char *modelName, const char *skinName ) {
  216.     char        filename[MAX_QPATH];
  217.  
  218.     Com_sprintf( filename, sizeof( filename ), "models/players/%s/lower_%s.skin", modelName, skinName );
  219.     ci->legsSkin = trap_R_RegisterSkin( filename );
  220.  
  221.     Com_sprintf( filename, sizeof( filename ), "models/players/%s/upper_%s.skin", modelName, skinName );
  222.     ci->torsoSkin = trap_R_RegisterSkin( filename );
  223.  
  224.     Com_sprintf( filename, sizeof( filename ), "models/players/%s/head_%s.skin", modelName, skinName );
  225.     ci->headSkin = trap_R_RegisterSkin( filename );
  226.  
  227.     if ( !ci->legsSkin || !ci->torsoSkin || !ci->headSkin ) {
  228.         return qfalse;
  229.     }
  230.  
  231.     return qtrue;
  232. }
  233.  
  234. /*
  235. ==========================
  236. CG_RegisterClientModelname
  237. ==========================
  238. */
  239. static qboolean CG_RegisterClientModelname( clientInfo_t *ci, const char *modelName, const char *skinName ) {
  240.     char        filename[MAX_QPATH];
  241.  
  242.     // load cmodels before models so filecache works
  243.  
  244.     Com_sprintf( filename, sizeof( filename ), "models/players/%s/lower.md3", modelName );
  245.     ci->legsModel = trap_R_RegisterModel( filename );
  246.     if ( !ci->legsModel ) {
  247.         Com_Printf( "Failed to load model file %s\n", filename );
  248.         return qfalse;
  249.     }
  250.  
  251.     Com_sprintf( filename, sizeof( filename ), "models/players/%s/upper.md3", modelName );
  252.     ci->torsoModel = trap_R_RegisterModel( filename );
  253.     if ( !ci->torsoModel ) {
  254.         Com_Printf( "Failed to load model file %s\n", filename );
  255.         return qfalse;
  256.     }
  257.  
  258.     Com_sprintf( filename, sizeof( filename ), "models/players/%s/head.md3", modelName );
  259.     ci->headModel = trap_R_RegisterModel( filename );
  260.     if ( !ci->headModel ) {
  261.         Com_Printf( "Failed to load model file %s\n", filename );
  262.         return qfalse;
  263.     }
  264.  
  265.     // if any skins failed to load, return failure
  266.     if ( !CG_RegisterClientSkin( ci, modelName, skinName ) ) {
  267.         Com_Printf( "Failed to load skin file: %s : %s\n", modelName, skinName );
  268.         return qfalse;
  269.     }
  270.  
  271.     // load the animations
  272.     Com_sprintf( filename, sizeof( filename ), "models/players/%s/animation.cfg", modelName );
  273.     if ( !CG_ParseAnimationFile( filename, ci ) ) {
  274.         Com_Printf( "Failed to load animation file %s\n", filename );
  275.         return qfalse;
  276.     }
  277.  
  278.     Com_sprintf( filename, sizeof( filename ), "models/players/%s/icon_%s.tga", modelName, skinName );
  279.     ci->modelIcon = trap_R_RegisterShaderNoMip( filename );
  280.     if ( !ci->modelIcon ) {
  281.         Com_Printf( "Failed to load icon file: %s\n", filename );
  282.         return qfalse;
  283.     }
  284.  
  285.     return qtrue;
  286. }
  287.  
  288. /*
  289. ====================
  290. CG_ColorFromString
  291. ====================
  292. */
  293. static void CG_ColorFromString( const char *v, vec3_t color ) {
  294.     int val;
  295.  
  296.     VectorClear( color );
  297.  
  298.     val = atoi( v );
  299.  
  300.     if ( val < 1 || val > 7 ) {
  301.         VectorSet( color, 1, 1, 1 );
  302.         return;
  303.     }
  304.  
  305.     if ( val & 1 ) {
  306.         color[2] = 1.0f;
  307.     }
  308.     if ( val & 2 ) {
  309.         color[1] = 1.0f;
  310.     }
  311.     if ( val & 4 ) {
  312.         color[0] = 1.0f;
  313.     }
  314. }
  315.  
  316. /*
  317. ===================
  318. CG_LoadClientInfo
  319.  
  320. Load it now, taking the disk hits.
  321. This will usually be deferred to a safe time
  322. ===================
  323. */
  324. static void CG_LoadClientInfo( clientInfo_t *ci ) {
  325.     const char    *dir, *fallback;
  326.     int            i;
  327.     const char    *s;
  328.     int            clientNum;
  329.  
  330.     if ( !CG_RegisterClientModelname( ci, ci->modelName, ci->skinName ) ) {
  331.         if ( cg_buildScript.integer ) {
  332.             CG_Error( "CG_RegisterClientModelname( %s, %s ) failed", ci->modelName, ci->skinName );
  333.         }
  334.  
  335.         // fall back
  336.         if ( cgs.gametype >= GT_TEAM ) {
  337.             // keep skin name
  338.             if ( !CG_RegisterClientModelname( ci, DEFAULT_MODEL, ci->skinName ) ) {
  339.                 CG_Error( "DEFAULT_MODEL / skin (%s/%s) failed to register",
  340.                     DEFAULT_MODEL, ci->skinName );
  341.             }
  342.         } else {
  343.             if ( !CG_RegisterClientModelname( ci, DEFAULT_MODEL, "default" ) ) {
  344.                 CG_Error( "DEFAULT_MODEL (%s) failed to register", DEFAULT_MODEL );
  345.             }
  346.         }
  347.     }
  348.  
  349.     // sounds
  350.     dir = ci->modelName;
  351.     fallback = DEFAULT_MODEL;
  352.  
  353.     for ( i = 0 ; i < MAX_CUSTOM_SOUNDS ; i++ ) {
  354.         s = cg_customSoundNames[i];
  355.         if ( !s ) {
  356.             break;
  357.         }
  358.         ci->sounds[i] = trap_S_RegisterSound( va("sound/player/%s/%s", dir, s + 1) );
  359.         if ( !ci->sounds[i] ) {
  360.             ci->sounds[i] = trap_S_RegisterSound( va("sound/player/%s/%s", fallback, s + 1) );
  361.         }
  362.     }
  363.  
  364.     ci->deferred = qfalse;
  365.  
  366.     // reset any existing players and bodies, because they might be in bad
  367.     // frames for this new model
  368.     clientNum = ci - cgs.clientinfo;
  369.     for ( i = 0 ; i < MAX_GENTITIES ; i++ ) {
  370.         if ( cg_entities[i].currentState.clientNum == clientNum
  371.             && cg_entities[i].currentState.eType == ET_PLAYER ) {
  372.             CG_ResetPlayerEntity( &cg_entities[i] );
  373.         }
  374.     }
  375. }
  376.  
  377. /*
  378. ======================
  379. CG_CopyClientInfoModel
  380. ======================
  381. */
  382. static void CG_CopyClientInfoModel( clientInfo_t *from, clientInfo_t *to ) {
  383.     VectorCopy( from->headOffset, to->headOffset );
  384.     to->footsteps = from->footsteps;
  385.     to->gender = from->gender;
  386.  
  387.     to->legsModel = from->legsModel;
  388.     to->legsSkin = from->legsSkin;
  389.     to->torsoModel = from->torsoModel;
  390.     to->torsoSkin = from->torsoSkin;
  391.     to->headModel = from->headModel;
  392.     to->headSkin = from->headSkin;
  393.     to->modelIcon = from->modelIcon;
  394.  
  395.     memcpy( to->animations, from->animations, sizeof( to->animations ) );
  396.     memcpy( to->sounds, from->sounds, sizeof( to->sounds ) );
  397. }
  398.  
  399. /*
  400. ======================
  401. CG_ScanForExistingClientInfo
  402. ======================
  403. */
  404. static qboolean CG_ScanForExistingClientInfo( clientInfo_t *ci ) {
  405.     int        i;
  406.     clientInfo_t    *match;
  407.  
  408.     for ( i = 0 ; i < cgs.maxclients ; i++ ) {
  409.         match = &cgs.clientinfo[ i ];
  410.         if ( !match->infoValid ) {
  411.             continue;
  412.         }
  413.         if ( match->deferred ) {
  414.             continue;
  415.         }
  416.         if ( !Q_stricmp( ci->modelName, match->modelName )
  417.             && !Q_stricmp( ci->skinName, match->skinName ) ) {
  418.             // this clientinfo is identical, so use it's handles
  419.  
  420.             ci->deferred = qfalse;
  421.  
  422.             CG_CopyClientInfoModel( match, ci );
  423.  
  424.             return qtrue;
  425.         }
  426.     }
  427.  
  428.     // nothing matches, so defer the load
  429.     return qfalse;
  430. }
  431.  
  432. /*
  433. ======================
  434. CG_SetDeferredClientInfo
  435.  
  436. We aren't going to load it now, so grab some other
  437. client's info to use until we have some spare time.
  438. ======================
  439. */
  440. static void CG_SetDeferredClientInfo( clientInfo_t *ci ) {
  441.     int        i;
  442.     clientInfo_t    *match;
  443.  
  444.     // if we are in teamplay, only grab a model if the skin is correct
  445.     if ( cgs.gametype >= GT_TEAM ) {
  446.         for ( i = 0 ; i < cgs.maxclients ; i++ ) {
  447.             match = &cgs.clientinfo[ i ];
  448.             if ( !match->infoValid ) {
  449.                 continue;
  450.             }
  451.             if ( Q_stricmp( ci->skinName, match->skinName ) ) {
  452.                 continue;
  453.             }
  454.             ci->deferred = qtrue;
  455.             CG_CopyClientInfoModel( match, ci );
  456.             return;
  457.         }
  458.  
  459.         // load the full model, because we don't ever want to show
  460.         // an improper team skin.  This will cause a hitch for the first
  461.         // player, when the second enters.  Combat shouldn't be going on
  462.         // yet, so it shouldn't matter
  463.         CG_LoadClientInfo( ci );
  464.         return;
  465.     }
  466.  
  467.     // find the first valid clientinfo and grab its stuff
  468.     for ( i = 0 ; i < cgs.maxclients ; i++ ) {
  469.         match = &cgs.clientinfo[ i ];
  470.         if ( !match->infoValid ) {
  471.             continue;
  472.         }
  473.  
  474.         ci->deferred = qtrue;
  475.         CG_CopyClientInfoModel( match, ci );
  476.         return;
  477.     }
  478.  
  479.     // we should never get here...
  480.     CG_Printf( "CG_SetDeferredClientInfo: no valid clients!\n" );
  481.  
  482.     CG_LoadClientInfo( ci );
  483. }
  484.  
  485.  
  486. /*
  487. ======================
  488. CG_NewClientInfo
  489. ======================
  490. */
  491. void CG_NewClientInfo( int clientNum ) {
  492.     clientInfo_t *ci;
  493.     clientInfo_t newInfo;
  494.     const char    *configstring;
  495.     const char    *v;
  496.     char        *slash;
  497.  
  498.     ci = &cgs.clientinfo[clientNum];
  499.  
  500.     configstring = CG_ConfigString( clientNum + CS_PLAYERS );
  501.     if ( !configstring[0] ) {
  502.         memset( ci, 0, sizeof( *ci ) );
  503.         return;        // player just left
  504.     }
  505.  
  506.     // build into a temp buffer so the defer checks can use
  507.     // the old value
  508.     memset( &newInfo, 0, sizeof( newInfo ) );
  509.  
  510.     // isolate the player's name
  511.     v = Info_ValueForKey(configstring, "n");
  512.     Q_strncpyz( newInfo.name, v, sizeof( newInfo.name ) );
  513.  
  514.     // colors
  515.     v = Info_ValueForKey( configstring, "c1" );
  516.     CG_ColorFromString( v, newInfo.color );
  517.  
  518.     // bot skill
  519.     v = Info_ValueForKey( configstring, "skill" );
  520.     newInfo.botSkill = atoi( v );
  521.  
  522.     // handicap
  523.     v = Info_ValueForKey( configstring, "hc" );
  524.     newInfo.handicap = atoi( v );
  525.  
  526.     // wins
  527.     v = Info_ValueForKey( configstring, "w" );
  528.     newInfo.wins = atoi( v );
  529.  
  530.     // losses
  531.     v = Info_ValueForKey( configstring, "l" );
  532.     newInfo.losses = atoi( v );
  533.  
  534.     // team
  535.     v = Info_ValueForKey( configstring, "t" );
  536.     newInfo.team = atoi( v );
  537.  
  538.     // model
  539.     v = Info_ValueForKey( configstring, "model" );
  540.     if ( cg_forceModel.integer ) {
  541.         // forcemodel makes everyone use a single model
  542.         // to prevent load hitches
  543.         char modelStr[MAX_QPATH];
  544.         char *skin;
  545.  
  546.  
  547.         trap_Cvar_VariableStringBuffer( "model", modelStr, sizeof( modelStr ) );
  548.         if ( ( skin = strchr( modelStr, '/' ) ) == NULL) {
  549.             skin = "default";
  550.         } else {
  551.             *skin++ = 0;
  552.         }
  553.  
  554.         Q_strncpyz( newInfo.skinName, skin, sizeof( newInfo.skinName ) );
  555.         Q_strncpyz( newInfo.modelName, modelStr, sizeof( newInfo.modelName ) );
  556.  
  557. //        Q_strncpyz( newInfo.modelName, DEFAULT_MODEL, sizeof( newInfo.modelName ) );
  558. //        Q_strncpyz( newInfo.skinName, "default", sizeof( newInfo.skinName ) );
  559.  
  560.         if ( cgs.gametype >= GT_TEAM ) {
  561.             // keep skin name
  562.             slash = strchr( v, '/' );
  563.             if ( slash ) {
  564.                 Q_strncpyz( newInfo.skinName, slash + 1, sizeof( newInfo.skinName ) );
  565.             }
  566.         }
  567.     } else {
  568.         Q_strncpyz( newInfo.modelName, v, sizeof( newInfo.modelName ) );
  569.  
  570.         slash = strchr( newInfo.modelName, '/' );
  571.         if ( !slash ) {
  572.             // modelName didn not include a skin name
  573.             Q_strncpyz( newInfo.skinName, "default", sizeof( newInfo.skinName ) );
  574.         } else {
  575.             Q_strncpyz( newInfo.skinName, slash + 1, sizeof( newInfo.skinName ) );
  576.             // truncate modelName
  577.             *slash = 0;
  578.         }
  579.     }
  580.  
  581.     // scan for an existing clientinfo that matches this modelname
  582.     // so we can avoid loading checks if possible
  583.     if ( !CG_ScanForExistingClientInfo( &newInfo ) ) {
  584.         qboolean    forceDefer;
  585.  
  586.         forceDefer = trap_MemoryRemaining() < 4000000;
  587.  
  588.         // if we are defering loads, just have it pick the first valid
  589.         if ( forceDefer || ( cg_deferPlayers.integer && !cg_buildScript.integer && !cg.loading ) ) {
  590.             // keep whatever they had if it won't violate team skins
  591.             if ( ci->infoValid && 
  592.                 ( cgs.gametype < GT_TEAM || !Q_stricmp( newInfo.skinName, ci->skinName ) ) ) {
  593.                 CG_CopyClientInfoModel( ci, &newInfo );
  594.                 newInfo.deferred = qtrue;
  595.             } else {
  596.                 // use whatever is available
  597.                 CG_SetDeferredClientInfo( &newInfo );
  598.             }
  599.             // if we are low on memory, leave them with this model
  600.             if ( forceDefer ) {
  601.                 CG_Printf( "Memory is low.  Using deferred model.\n" );
  602.                 newInfo.deferred = qfalse;
  603.             }
  604.         } else {
  605.             CG_LoadClientInfo( &newInfo );
  606.         }
  607.     }
  608.  
  609.     // replace whatever was there with the new one
  610.     newInfo.infoValid = qtrue;
  611.     *ci = newInfo;
  612. }
  613.  
  614.  
  615.  
  616. /*
  617. ======================
  618. CG_LoadDeferredPlayers
  619.  
  620. Called each frame when a player is dead
  621. and the scoreboard is up
  622. so deferred players can be loaded
  623. ======================
  624. */
  625. void CG_LoadDeferredPlayers( void ) {
  626.     int        i;
  627.     clientInfo_t    *ci;
  628.  
  629.     // scan for a deferred player to load
  630.     for ( i = 0, ci = cgs.clientinfo ; i < cgs.maxclients ; i++, ci++ ) {
  631.         if ( ci->infoValid && ci->deferred ) {
  632.             // if we are low on memory, leave it deferred
  633.             if ( trap_MemoryRemaining() < 4000000 ) {
  634.                 CG_Printf( "Memory is low.  Using deferred model.\n" );
  635.                 ci->deferred = qfalse;
  636.                 continue;
  637.             }
  638.             CG_LoadClientInfo( ci );
  639. //            break;
  640.         }
  641.     }
  642. }
  643.  
  644. /*
  645. =============================================================================
  646.  
  647. PLAYER ANIMATION
  648.  
  649. =============================================================================
  650. */
  651.  
  652.  
  653. /*
  654. ===============
  655. CG_SetLerpFrameAnimation
  656.  
  657. may include ANIM_TOGGLEBIT
  658. ===============
  659. */
  660. static void CG_SetLerpFrameAnimation( clientInfo_t *ci, lerpFrame_t *lf, int newAnimation ) {
  661.     animation_t    *anim;
  662.  
  663.     lf->animationNumber = newAnimation;
  664.     newAnimation &= ~ANIM_TOGGLEBIT;
  665.  
  666.     if ( newAnimation < 0 || newAnimation >= MAX_ANIMATIONS ) {
  667.         CG_Error( "Bad animation number: %i", newAnimation );
  668.     }
  669.  
  670.     anim = &ci->animations[ newAnimation ];
  671.  
  672.     lf->animation = anim;
  673.     lf->animationTime = lf->frameTime + anim->initialLerp;
  674.  
  675.     if ( cg_debugAnim.integer ) {
  676.         CG_Printf( "Anim: %i\n", newAnimation );
  677.     }
  678. }
  679.  
  680. /*
  681. ===============
  682. CG_RunLerpFrame
  683.  
  684. Sets cg.snap, cg.oldFrame, and cg.backlerp
  685. cg.time should be between oldFrameTime and frameTime after exit
  686. ===============
  687. */
  688. static void CG_RunLerpFrame( clientInfo_t *ci, lerpFrame_t *lf, int newAnimation, float speedScale ) {
  689.     int            f;
  690.     animation_t    *anim;
  691.  
  692.     // debugging tool to get no animations
  693.     if ( cg_animSpeed.integer == 0 ) {
  694.         lf->oldFrame = lf->frame = lf->backlerp = 0;
  695.         return;
  696.     }
  697.  
  698.     // see if the animation sequence is switching
  699.     if ( newAnimation != lf->animationNumber || !lf->animation ) {
  700.         CG_SetLerpFrameAnimation( ci, lf, newAnimation );
  701.     }
  702.  
  703.     // if we have passed the current frame, move it to
  704.     // oldFrame and calculate a new frame
  705.     if ( cg.time >= lf->frameTime ) {
  706.         lf->oldFrame = lf->frame;
  707.         lf->oldFrameTime = lf->frameTime;
  708.  
  709.         // get the next frame based on the animation
  710.         anim = lf->animation;
  711.         if ( !anim->frameLerp ) {
  712.             return;        // shouldn't happen
  713.         }
  714.         if ( cg.time < lf->animationTime ) {
  715.             lf->frameTime = lf->animationTime;        // initial lerp
  716.         } else {
  717.             lf->frameTime = lf->oldFrameTime + anim->frameLerp;
  718.         }
  719.         f = ( lf->frameTime - lf->animationTime ) / anim->frameLerp;
  720.         f *= speedScale;        // adjust for haste, etc
  721.         if ( f >= anim->numFrames ) {
  722.             f -= anim->numFrames;
  723.             if ( anim->loopFrames ) {
  724.                 f %= anim->loopFrames;
  725.                 f += anim->numFrames - anim->loopFrames;
  726.             } else {
  727.                 f = anim->numFrames - 1;
  728.                 // the animation is stuck at the end, so it
  729.                 // can immediately transition to another sequence
  730.                 lf->frameTime = cg.time;
  731.             }
  732.         }
  733.         lf->frame = anim->firstFrame + f;
  734.         if ( cg.time > lf->frameTime ) {
  735.             lf->frameTime = cg.time;
  736.             if ( cg_debugAnim.integer ) {
  737.                 CG_Printf( "Clamp lf->frameTime\n");
  738.             }
  739.         }
  740.     }
  741.  
  742.     if ( lf->frameTime > cg.time + 200 ) {
  743.         lf->frameTime = cg.time;
  744.     }
  745.  
  746.     if ( lf->oldFrameTime > cg.time ) {
  747.         lf->oldFrameTime = cg.time;
  748.     }
  749.     // calculate current lerp value
  750.     if ( lf->frameTime == lf->oldFrameTime ) {
  751.         lf->backlerp = 0;
  752.     } else {
  753.         lf->backlerp = 1.0 - (float)( cg.time - lf->oldFrameTime ) / ( lf->frameTime - lf->oldFrameTime );
  754.     }
  755. }
  756.  
  757.  
  758. /*
  759. ===============
  760. CG_ClearLerpFrame
  761. ===============
  762. */
  763. static void CG_ClearLerpFrame( clientInfo_t *ci, lerpFrame_t *lf, int animationNumber ) {
  764.     lf->frameTime = lf->oldFrameTime = cg.time;
  765.     CG_SetLerpFrameAnimation( ci, lf, animationNumber );
  766.     lf->oldFrame = lf->frame = lf->animation->firstFrame;
  767. }
  768.  
  769.  
  770. /*
  771. ===============
  772. CG_PlayerAnimation
  773. ===============
  774. */
  775. static void CG_PlayerAnimation( centity_t *cent, int *legsOld, int *legs, float *legsBackLerp,
  776.                         int *torsoOld, int *torso, float *torsoBackLerp ) {
  777.     clientInfo_t    *ci;
  778.     int                clientNum;
  779.     float            speedScale;
  780.  
  781.     clientNum = cent->currentState.clientNum;
  782.  
  783.     if ( cg_noPlayerAnims.integer ) {
  784.         *legsOld = *legs = *torsoOld = *torso = 0;
  785.         return;
  786.     }
  787.  
  788.     if ( cent->currentState.powerups & ( 1 << PW_HASTE ) ) {
  789.         speedScale = 1.5;
  790.     } else {
  791.         speedScale = 1;
  792.     }
  793.  
  794.     ci = &cgs.clientinfo[ clientNum ];
  795.  
  796.     // do the shuffle turn frames locally
  797.     if ( cent->pe.legs.yawing && ( cent->currentState.legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_IDLE ) {
  798.         CG_RunLerpFrame( ci, ¢->pe.legs, LEGS_TURN, speedScale );
  799.     } else {
  800.         CG_RunLerpFrame( ci, ¢->pe.legs, cent->currentState.legsAnim, speedScale );
  801.     }
  802.  
  803.     *legsOld = cent->pe.legs.oldFrame;
  804.     *legs = cent->pe.legs.frame;
  805.     *legsBackLerp = cent->pe.legs.backlerp;
  806.  
  807.     CG_RunLerpFrame( ci, ¢->pe.torso, cent->currentState.torsoAnim, speedScale );
  808.  
  809.     *torsoOld = cent->pe.torso.oldFrame;
  810.     *torso = cent->pe.torso.frame;
  811.     *torsoBackLerp = cent->pe.torso.backlerp;
  812. }
  813.  
  814. /*
  815. =============================================================================
  816.  
  817. PLAYER ANGLES
  818.  
  819. =============================================================================
  820. */
  821.  
  822. /*
  823. ==================
  824. CG_SwingAngles
  825. ==================
  826. */
  827. static void CG_SwingAngles( float destination, float swingTolerance, float clampTolerance,
  828.                     float speed, float *angle, qboolean *swinging ) {
  829.     float    swing;
  830.     float    move;
  831.     float    scale;
  832.  
  833.     if ( !*swinging ) {
  834.         // see if a swing should be started
  835.         swing = AngleSubtract( *angle, destination );
  836.         if ( swing > swingTolerance || swing < -swingTolerance ) {
  837.             *swinging = qtrue;
  838.         }
  839.     }
  840.  
  841.     if ( !*swinging ) {
  842.         return;
  843.     }
  844.     
  845.     // modify the speed depending on the delta
  846.     // so it doesn't seem so linear
  847.     swing = AngleSubtract( destination, *angle );
  848.     scale = fabs( swing );
  849.     if ( scale < swingTolerance * 0.5 ) {
  850.         scale = 0.5;
  851.     } else if ( scale < swingTolerance ) {
  852.         scale = 1.0;
  853.     } else {
  854.         scale = 2.0;
  855.     }
  856.  
  857.     // swing towards the destination angle
  858.     if ( swing >= 0 ) {
  859.         move = cg.frametime * scale * speed;
  860.         if ( move >= swing ) {
  861.             move = swing;
  862.             *swinging = qfalse;
  863.         }
  864.         *angle = AngleMod( *angle + move );
  865.     } else if ( swing < 0 ) {
  866.         move = cg.frametime * scale * -speed;
  867.         if ( move <= swing ) {
  868.             move = swing;
  869.             *swinging = qfalse;
  870.         }
  871.         *angle = AngleMod( *angle + move );
  872.     }
  873.  
  874.     // clamp to no more than tolerance
  875.     swing = AngleSubtract( destination, *angle );
  876.     if ( swing > clampTolerance ) {
  877.         *angle = AngleMod( destination - (clampTolerance - 1) );
  878.     } else if ( swing < -clampTolerance ) {
  879.         *angle = AngleMod( destination + (clampTolerance - 1) );
  880.     }
  881. }
  882.  
  883. /*
  884. =================
  885. CG_AddPainTwitch
  886. =================
  887. */
  888. static void CG_AddPainTwitch( centity_t *cent, vec3_t torsoAngles ) {
  889.     int        t;
  890.     float    f;
  891.  
  892.     t = cg.time - cent->pe.painTime;
  893.     if ( t >= PAIN_TWITCH_TIME ) {
  894.         return;
  895.     }
  896.  
  897.     f = 1.0 - (float)t / PAIN_TWITCH_TIME;
  898.  
  899.     if ( cent->pe.painDirection ) {
  900.         torsoAngles[ROLL] += 20 * f;
  901.     } else {
  902.         torsoAngles[ROLL] -= 20 * f;
  903.     }
  904. }
  905.  
  906.  
  907. /*
  908. ===============
  909. CG_PlayerAngles
  910.  
  911. Handles seperate torso motion
  912.  
  913.   legs pivot based on direction of movement
  914.  
  915.   head always looks exactly at cent->lerpAngles
  916.  
  917.   if motion < 20 degrees, show in head only
  918.   if < 45 degrees, also show in torso
  919. ===============
  920. */
  921. static void CG_PlayerAngles( centity_t *cent, vec3_t legs[3], vec3_t torso[3], vec3_t head[3] ) {
  922.     vec3_t        legsAngles, torsoAngles, headAngles;
  923.     float        dest;
  924.     static    int    movementOffsets[8] = { 0, 22, 45, -22, 0, 22, -45, -22 };
  925.     vec3_t        velocity;
  926.     float        speed;
  927.     int            dir;
  928.  
  929.     VectorCopy( cent->lerpAngles, headAngles );
  930.     headAngles[YAW] = AngleMod( headAngles[YAW] );
  931.     VectorClear( legsAngles );
  932.     VectorClear( torsoAngles );
  933.  
  934.     // --------- yaw -------------
  935.  
  936.     // allow yaw to drift a bit
  937.     if ( ( cent->currentState.legsAnim & ~ANIM_TOGGLEBIT ) != LEGS_IDLE 
  938.         || ( cent->currentState.torsoAnim & ~ANIM_TOGGLEBIT ) != TORSO_STAND  ) {
  939.         // if not standing still, always point all in the same direction
  940.         cent->pe.torso.yawing = qtrue;    // always center
  941.         cent->pe.torso.pitching = qtrue;    // always center
  942.         cent->pe.legs.yawing = qtrue;    // always center
  943.     }
  944.  
  945.     // adjust legs for movement dir
  946.     if ( cent->currentState.eFlags & EF_DEAD ) {
  947.         // don't let dead bodies twitch
  948.         dir = 0;
  949.     } else {
  950.         dir = cent->currentState.angles2[YAW];
  951.         if ( dir < 0 || dir > 7 ) {
  952.             CG_Error( "Bad player movement angle" );
  953.         }
  954.     }
  955.     legsAngles[YAW] = headAngles[YAW] + movementOffsets[ dir ];
  956.     torsoAngles[YAW] = headAngles[YAW] + 0.25 * movementOffsets[ dir ];
  957.  
  958.     // torso
  959.     CG_SwingAngles( torsoAngles[YAW], 25, 90, cg_swingSpeed.value, ¢->pe.torso.yawAngle, ¢->pe.torso.yawing );
  960.     CG_SwingAngles( legsAngles[YAW], 40, 90, cg_swingSpeed.value, ¢->pe.legs.yawAngle, ¢->pe.legs.yawing );
  961.  
  962.     torsoAngles[YAW] = cent->pe.torso.yawAngle;
  963.     legsAngles[YAW] = cent->pe.legs.yawAngle;
  964.  
  965.     // --------- pitch -------------
  966.  
  967.     // only show a fraction of the pitch angle in the torso
  968.     if ( headAngles[PITCH] > 180 ) {
  969.         dest = (-360 + headAngles[PITCH]) * 0.75;
  970.     } else {
  971.         dest = headAngles[PITCH] * 0.75;
  972.     }
  973.     CG_SwingAngles( dest, 15, 30, 0.1, ¢->pe.torso.pitchAngle, ¢->pe.torso.pitching );
  974.     torsoAngles[PITCH] = cent->pe.torso.pitchAngle;
  975.  
  976.     // --------- roll -------------
  977.  
  978.  
  979.     // lean towards the direction of travel
  980.     VectorCopy( cent->currentState.pos.trDelta, velocity );
  981.     speed = VectorNormalize( velocity );
  982.     if ( speed ) {
  983.         vec3_t    axis[3];
  984.         float    side;
  985.  
  986.         speed *= 0.05;
  987.  
  988.         AnglesToAxis( legsAngles, axis );
  989.         side = speed * DotProduct( velocity, axis[1] );
  990.         legsAngles[ROLL] -= side;
  991.  
  992.         side = speed * DotProduct( velocity, axis[0] );
  993.         legsAngles[PITCH] += side;
  994.     }
  995.  
  996.     // pain twitch
  997.     CG_AddPainTwitch( cent, torsoAngles );
  998.  
  999.     // pull the angles back out of the hierarchial chain
  1000.     AnglesSubtract( headAngles, torsoAngles, headAngles );
  1001.     AnglesSubtract( torsoAngles, legsAngles, torsoAngles );
  1002.     AnglesToAxis( legsAngles, legs );
  1003.     AnglesToAxis( torsoAngles, torso );
  1004.     AnglesToAxis( headAngles, head );
  1005. }
  1006.  
  1007.  
  1008. //==========================================================================
  1009.  
  1010. /*
  1011. ===============
  1012. CG_HasteTrail
  1013. ===============
  1014. */
  1015. static void CG_HasteTrail( centity_t *cent ) {
  1016.     localEntity_t    *smoke;
  1017.     vec3_t            origin;
  1018.     int                anim;
  1019.  
  1020.     if ( cent->trailTime > cg.time ) {
  1021.         return;
  1022.     }
  1023.     anim = cent->pe.legs.animationNumber & ~ANIM_TOGGLEBIT;
  1024.     if ( anim != LEGS_RUN && anim != LEGS_BACK ) {
  1025.         return;
  1026.     }
  1027.  
  1028.     cent->trailTime += 100;
  1029.     if ( cent->trailTime < cg.time ) {
  1030.         cent->trailTime = cg.time;
  1031.     }
  1032.  
  1033.     VectorCopy( cent->lerpOrigin, origin );
  1034.     origin[2] -= 16;
  1035.  
  1036.     smoke = CG_SmokePuff( origin, vec3_origin, 
  1037.                   8, 
  1038.                   1, 1, 1, 1,
  1039.                   500, 
  1040.                   cg.time,
  1041.                   0, 
  1042.                   cgs.media.hastePuffShader );
  1043.  
  1044.     // use the optimized local entity add
  1045.     smoke->leType = LE_SCALE_FADE;
  1046. }
  1047.  
  1048. /*
  1049. ===============
  1050. CG_TrailItem
  1051. ===============
  1052. */
  1053. static void CG_TrailItem( centity_t *cent, qhandle_t hModel ) {
  1054.     refEntity_t        ent;
  1055.     vec3_t            angles;
  1056.     vec3_t            axis[3];
  1057.  
  1058.     VectorCopy( cent->lerpAngles, angles );
  1059.     angles[PITCH] = 0;
  1060.     angles[ROLL] = 0;
  1061.     AnglesToAxis( angles, axis );
  1062.  
  1063.     memset( &ent, 0, sizeof( ent ) );
  1064.     VectorMA( cent->lerpOrigin, -16, axis[0], ent.origin );
  1065.     ent.origin[2] += 16;
  1066. #if 0
  1067.     VectorScale( cg.autoAxis[0], 0.75, ent.axis[0] );
  1068.     VectorScale( cg.autoAxis[1], 0.75, ent.axis[1] );
  1069.     VectorScale( cg.autoAxis[2], 0.75, ent.axis[2] );
  1070. #else
  1071.     angles[YAW] += 90;
  1072.     AnglesToAxis( angles, ent.axis );
  1073. #endif
  1074.  
  1075.     ent.hModel = hModel;
  1076.     trap_R_AddRefEntityToScene( &ent );
  1077. }
  1078.  
  1079.  
  1080. /*
  1081. ===============
  1082. CG_PlayerPowerups
  1083. ===============
  1084. */
  1085. static void CG_PlayerPowerups( centity_t *cent ) {
  1086.     int        powerups;
  1087.  
  1088.     powerups = cent->currentState.powerups;
  1089.     if ( !powerups ) {
  1090.         return;
  1091.     }
  1092.  
  1093.     // quad gives a dlight
  1094.     if ( powerups & ( 1 << PW_QUAD ) ) {
  1095.         trap_R_AddLightToScene( cent->lerpOrigin, 200 + (rand()&31), 0.2, 0.2, 1 );
  1096.     }
  1097.  
  1098.     // flight plays a looped sound
  1099.     if ( powerups & ( 1 << PW_FLIGHT ) ) {
  1100.         trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, cgs.media.flightSound );
  1101.     }
  1102.  
  1103.     // redflag
  1104.     if ( powerups & ( 1 << PW_REDFLAG ) ) {
  1105.         CG_TrailItem( cent, cgs.media.redFlagModel );
  1106.         trap_R_AddLightToScene( cent->lerpOrigin, 200 + (rand()&31), 1, 0.2, 0.2 );
  1107.     }
  1108.  
  1109.     // blueflag
  1110.     if ( powerups & ( 1 << PW_BLUEFLAG ) ) {
  1111.         CG_TrailItem( cent, cgs.media.blueFlagModel );
  1112.         trap_R_AddLightToScene( cent->lerpOrigin, 200 + (rand()&31), 0.2, 0.2, 1 );
  1113.     }
  1114.  
  1115.     // haste leaves smoke trails
  1116.     if ( powerups & ( 1 << PW_HASTE ) ) {
  1117.         CG_HasteTrail( cent );
  1118.     }
  1119. }
  1120.  
  1121.  
  1122. /*
  1123. ===============
  1124. CG_PlayerFloatSprite
  1125.  
  1126. Float a sprite over the player's head
  1127. ===============
  1128. */
  1129. static void CG_PlayerFloatSprite( centity_t *cent, qhandle_t shader ) {
  1130.     int                rf;
  1131.     refEntity_t        ent;
  1132.  
  1133.     if ( cent->currentState.number == cg.snap->ps.clientNum && !cg.renderingThirdPerson ) {
  1134.         rf = RF_THIRD_PERSON;        // only show in mirrors
  1135.     } else {
  1136.         rf = 0;
  1137.     }
  1138.  
  1139.     memset( &ent, 0, sizeof( ent ) );
  1140.     VectorCopy( cent->lerpOrigin, ent.origin );
  1141.     ent.origin[2] += 48;
  1142.     ent.reType = RT_SPRITE;
  1143.     ent.customShader = shader;
  1144.     ent.radius = 10;
  1145.     ent.renderfx = rf;
  1146.     ent.shaderRGBA[0] = 255;
  1147.     ent.shaderRGBA[1] = 255;
  1148.     ent.shaderRGBA[2] = 255;
  1149.     ent.shaderRGBA[3] = 255;
  1150.     trap_R_AddRefEntityToScene( &ent );
  1151. }
  1152.  
  1153.  
  1154.  
  1155. /*
  1156. ===============
  1157. CG_PlayerSprites
  1158.  
  1159. Float sprites over the player's head
  1160. ===============
  1161. */
  1162. static void CG_PlayerSprites( centity_t *cent ) {
  1163.     int        team;
  1164.  
  1165.     if ( cent->currentState.eFlags & EF_CONNECTION ) {
  1166.         CG_PlayerFloatSprite( cent, cgs.media.connectionShader );
  1167.         return;
  1168.     }
  1169.  
  1170.     if ( cent->currentState.eFlags & EF_TALK ) {
  1171.         CG_PlayerFloatSprite( cent, cgs.media.balloonShader );
  1172.         return;
  1173.     }
  1174.  
  1175.     if ( cent->currentState.eFlags & EF_AWARD_IMPRESSIVE ) {
  1176.         CG_PlayerFloatSprite( cent, cgs.media.medalImpressive );
  1177.         return;
  1178.     }
  1179.  
  1180.     if ( cent->currentState.eFlags & EF_AWARD_EXCELLENT ) {
  1181.         CG_PlayerFloatSprite( cent, cgs.media.medalExcellent );
  1182.         return;
  1183.     }
  1184.  
  1185.     if ( cent->currentState.eFlags & EF_AWARD_GAUNTLET ) {
  1186.         CG_PlayerFloatSprite( cent, cgs.media.medalGauntlet );
  1187.         return;
  1188.     }
  1189.  
  1190.     team = cgs.clientinfo[ cent->currentState.clientNum ].team;
  1191.     if ( !(cent->currentState.eFlags & EF_DEAD) && 
  1192.         cg.snap->ps.persistant[PERS_TEAM] == team &&
  1193.         cgs.gametype >= GT_TEAM) {
  1194.         CG_PlayerFloatSprite( cent, cgs.media.friendShader );
  1195.         return;
  1196.     }
  1197. }
  1198.  
  1199. /*
  1200. ===============
  1201. CG_PlayerShadow
  1202.  
  1203. Returns the Z component of the surface being shadowed
  1204.  
  1205.   should it return a full plane instead of a Z?
  1206. ===============
  1207. */
  1208. #define    SHADOW_DISTANCE        128
  1209. static qboolean CG_PlayerShadow( centity_t *cent, float *shadowPlane ) {
  1210.     vec3_t        end, mins = {-15, -15, 0}, maxs = {15, 15, 2};
  1211.     trace_t        trace;
  1212.     float        alpha;
  1213.  
  1214.     *shadowPlane = 0;
  1215.  
  1216.     if ( cg_shadows.integer == 0 ) {
  1217.         return qfalse;
  1218.     }
  1219.  
  1220.     // no shadows when invisible
  1221.     if ( cent->currentState.powerups & ( 1 << PW_INVIS ) ) {
  1222.         return qfalse;
  1223.     }
  1224.  
  1225.     // send a trace down from the player to the ground
  1226.     VectorCopy( cent->lerpOrigin, end );
  1227.     end[2] -= SHADOW_DISTANCE;
  1228.  
  1229.     trap_CM_BoxTrace( &trace, cent->lerpOrigin, end, mins, maxs, 0, MASK_PLAYERSOLID );
  1230.  
  1231.     // no shadow if too high
  1232.     if ( trace.fraction == 1.0 ) {
  1233.         return qfalse;
  1234.     }
  1235.  
  1236.     *shadowPlane = trace.endpos[2] + 1;
  1237.  
  1238.     if ( cg_shadows.integer != 1 ) {    // no mark for stencil or projection shadows
  1239.         return qtrue;
  1240.     }
  1241.  
  1242.     // fade the shadow out with height
  1243.     alpha = 1.0 - trace.fraction;
  1244.  
  1245.     // add the mark as a temporary, so it goes directly to the renderer
  1246.     // without taking a spot in the cg_marks array
  1247.     CG_ImpactMark( cgs.media.shadowMarkShader, trace.endpos, trace.plane.normal, 
  1248.         cent->pe.legs.yawAngle, alpha,alpha,alpha,1, qfalse, 24, qtrue );
  1249.  
  1250.     return qtrue;
  1251. }
  1252.  
  1253.  
  1254. /*
  1255. ===============
  1256. CG_PlayerSplash
  1257.  
  1258. Draw a mark at the water surface
  1259. ===============
  1260. */
  1261. static void CG_PlayerSplash( centity_t *cent ) {
  1262.     vec3_t        start, end;
  1263.     trace_t        trace;
  1264.     int            contents;
  1265.     polyVert_t    verts[4];
  1266.  
  1267.     if ( !cg_shadows.integer ) {
  1268.         return;
  1269.     }
  1270.  
  1271.     VectorCopy( cent->lerpOrigin, end );
  1272.     end[2] -= 24;
  1273.  
  1274.     // if the feet aren't in liquid, don't make a mark
  1275.     // this won't handle moving water brushes, but they wouldn't draw right anyway...
  1276.     contents = trap_CM_PointContents( end, 0 );
  1277.     if ( !( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) ) {
  1278.         return;
  1279.     }
  1280.  
  1281.     VectorCopy( cent->lerpOrigin, start );
  1282.     start[2] += 32;
  1283.  
  1284.     // if the head isn't out of liquid, don't make a mark
  1285.     contents = trap_CM_PointContents( start, 0 );
  1286.     if ( contents & ( CONTENTS_SOLID | CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) {
  1287.         return;
  1288.     }
  1289.  
  1290.     // trace down to find the surface
  1291.     trap_CM_BoxTrace( &trace, start, end, NULL, NULL, 0, ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) );
  1292.  
  1293.     if ( trace.fraction == 1.0 ) {
  1294.         return;
  1295.     }
  1296.  
  1297.     // create a mark polygon
  1298.     VectorCopy( trace.endpos, verts[0].xyz );
  1299.     verts[0].xyz[0] -= 32;
  1300.     verts[0].xyz[1] -= 32;
  1301.     verts[0].st[0] = 0;
  1302.     verts[0].st[1] = 0;
  1303.     verts[0].modulate[0] = 255;
  1304.     verts[0].modulate[1] = 255;
  1305.     verts[0].modulate[2] = 255;
  1306.     verts[0].modulate[3] = 255;
  1307.  
  1308.     VectorCopy( trace.endpos, verts[1].xyz );
  1309.     verts[1].xyz[0] -= 32;
  1310.     verts[1].xyz[1] += 32;
  1311.     verts[1].st[0] = 0;
  1312.     verts[1].st[1] = 1;
  1313.     verts[1].modulate[0] = 255;
  1314.     verts[1].modulate[1] = 255;
  1315.     verts[1].modulate[2] = 255;
  1316.     verts[1].modulate[3] = 255;
  1317.  
  1318.     VectorCopy( trace.endpos, verts[2].xyz );
  1319.     verts[2].xyz[0] += 32;
  1320.     verts[2].xyz[1] += 32;
  1321.     verts[2].st[0] = 1;
  1322.     verts[2].st[1] = 1;
  1323.     verts[2].modulate[0] = 255;
  1324.     verts[2].modulate[1] = 255;
  1325.     verts[2].modulate[2] = 255;
  1326.     verts[2].modulate[3] = 255;
  1327.  
  1328.     VectorCopy( trace.endpos, verts[3].xyz );
  1329.     verts[3].xyz[0] += 32;
  1330.     verts[3].xyz[1] -= 32;
  1331.     verts[3].st[0] = 1;
  1332.     verts[3].st[1] = 0;
  1333.     verts[3].modulate[0] = 255;
  1334.     verts[3].modulate[1] = 255;
  1335.     verts[3].modulate[2] = 255;
  1336.     verts[3].modulate[3] = 255;
  1337.  
  1338.     trap_R_AddPolyToScene( cgs.media.wakeMarkShader, 4, verts );
  1339. }
  1340.  
  1341.  
  1342.  
  1343. /*
  1344. ===============
  1345. CG_AddRefEntityWithPowerups
  1346.  
  1347. Adds a piece with modifications or duplications for powerups
  1348. Also called by CG_Missile for quad rockets, but nobody can tell...
  1349. ===============
  1350. */
  1351. void CG_AddRefEntityWithPowerups( refEntity_t *ent, int powerups, int team ) {
  1352.  
  1353.     if ( powerups & ( 1 << PW_INVIS ) ) {
  1354.         ent->customShader = cgs.media.invisShader;
  1355.         trap_R_AddRefEntityToScene( ent );
  1356.     } else {
  1357.         trap_R_AddRefEntityToScene( ent );
  1358.  
  1359.         if ( powerups & ( 1 << PW_QUAD ) ) 
  1360.         {
  1361.             if (team == TEAM_RED)
  1362.                 ent->customShader = cgs.media.redQuadShader;
  1363.             else
  1364.                 ent->customShader = cgs.media.quadShader;
  1365.             trap_R_AddRefEntityToScene( ent );
  1366.         }
  1367.         if ( powerups & ( 1 << PW_REGEN ) ) {
  1368.             if ( ( ( cg.time / 100 ) % 10 ) == 1 ) {
  1369.                 ent->customShader = cgs.media.regenShader;
  1370.                 trap_R_AddRefEntityToScene( ent );
  1371.             }
  1372.         }
  1373.         if ( powerups & ( 1 << PW_BATTLESUIT ) ) {
  1374.             ent->customShader = cgs.media.battleSuitShader;
  1375.             trap_R_AddRefEntityToScene( ent );
  1376.         }
  1377.     }
  1378. }
  1379.  
  1380. /*
  1381. ===============
  1382. CG_Player
  1383. ===============
  1384. */
  1385. void CG_Player( centity_t *cent ) {
  1386.     clientInfo_t    *ci;
  1387.     refEntity_t        legs;
  1388.     refEntity_t        torso;
  1389.     refEntity_t        head;
  1390.     int                clientNum;
  1391.     int                renderfx;
  1392.     qboolean        shadow;
  1393.     float            shadowPlane;
  1394.  
  1395.     // the client number is stored in clientNum.  It can't be derived
  1396.     // from the entity number, because a single client may have
  1397.     // multiple corpses on the level using the same clientinfo
  1398.     clientNum = cent->currentState.clientNum;
  1399.     if ( clientNum < 0 || clientNum >= MAX_CLIENTS ) {
  1400.         CG_Error( "Bad clientNum on player entity");
  1401.     }
  1402.     ci = &cgs.clientinfo[ clientNum ];
  1403.  
  1404.     // it is possible to see corpses from disconnected players that may
  1405.     // not have valid clientinfo
  1406.     if ( !ci->infoValid ) {
  1407.         return;
  1408.     }
  1409.  
  1410.     memset( &legs, 0, sizeof(legs) );
  1411.     memset( &torso, 0, sizeof(torso) );
  1412.     memset( &head, 0, sizeof(head) );
  1413.  
  1414.     // get the rotation information
  1415.     CG_PlayerAngles( cent, legs.axis, torso.axis, head.axis );
  1416.     
  1417.     // get the animation state (after rotation, to allow feet shuffle)
  1418.     CG_PlayerAnimation( cent, &legs.oldframe, &legs.frame, &legs.backlerp,
  1419.          &torso.oldframe, &torso.frame, &torso.backlerp );
  1420.  
  1421.     // add powerups floating behind the player
  1422.     CG_PlayerPowerups( cent );
  1423.  
  1424.     // add the talk baloon or disconnect icon
  1425.     CG_PlayerSprites( cent );
  1426.  
  1427.     // add the shadow
  1428.     shadow = CG_PlayerShadow( cent, &shadowPlane );
  1429.  
  1430.     // add a water splash if partially in and out of water
  1431.     CG_PlayerSplash( cent );
  1432.  
  1433.     // get the player model information
  1434.     renderfx = 0;
  1435.     if ( cent->currentState.number == cg.snap->ps.clientNum && !cg.renderingThirdPerson ) {
  1436.         renderfx = RF_THIRD_PERSON;            // only draw in mirrors
  1437.     }
  1438.     if ( cg_shadows.integer == 3 && shadow ) {
  1439.         renderfx |= RF_SHADOW_PLANE;
  1440.     }
  1441.     renderfx |= RF_LIGHTING_ORIGIN;            // use the same origin for all
  1442.  
  1443.     //
  1444.     // add the legs
  1445.     //
  1446.     legs.hModel = ci->legsModel;
  1447.     legs.customSkin = ci->legsSkin;
  1448.  
  1449.     VectorCopy( cent->lerpOrigin, legs.origin );
  1450.  
  1451.     VectorCopy( cent->lerpOrigin, legs.lightingOrigin );
  1452.     legs.shadowPlane = shadowPlane;
  1453.     legs.renderfx = renderfx;
  1454.     VectorCopy (legs.origin, legs.oldorigin);    // don't positionally lerp at all
  1455.  
  1456.     CG_AddRefEntityWithPowerups( &legs, cent->currentState.powerups, ci->team );
  1457.  
  1458.     // if the model failed, allow the default nullmodel to be displayed
  1459.     if (!legs.hModel) {
  1460.         return;
  1461.     }
  1462.  
  1463.     //
  1464.     // add the torso
  1465.     //
  1466.     torso.hModel = ci->torsoModel;
  1467.     if (!torso.hModel) {
  1468.         return;
  1469.     }
  1470.  
  1471.     torso.customSkin = ci->torsoSkin;
  1472.  
  1473.     VectorCopy( cent->lerpOrigin, torso.lightingOrigin );
  1474.  
  1475.     CG_PositionRotatedEntityOnTag( &torso, &legs, ci->legsModel, "tag_torso");
  1476.  
  1477.     torso.shadowPlane = shadowPlane;
  1478.     torso.renderfx = renderfx;
  1479.  
  1480.     CG_AddRefEntityWithPowerups( &torso, cent->currentState.powerups, ci->team );
  1481.  
  1482.     //
  1483.     // add the head
  1484.     //
  1485.     head.hModel = ci->headModel;
  1486.     if (!head.hModel) {
  1487.         return;
  1488.     }
  1489.     head.customSkin = ci->headSkin;
  1490.  
  1491.     VectorCopy( cent->lerpOrigin, head.lightingOrigin );
  1492.  
  1493.     CG_PositionRotatedEntityOnTag( &head, &torso, ci->torsoModel, "tag_head");
  1494.  
  1495.     head.shadowPlane = shadowPlane;
  1496.     head.renderfx = renderfx;
  1497.  
  1498.     CG_AddRefEntityWithPowerups( &head, cent->currentState.powerups, ci->team );
  1499.  
  1500.     //
  1501.     // add the gun / barrel / flash
  1502.     //
  1503.     CG_AddPlayerWeapon( &torso, NULL, cent );
  1504. }
  1505.  
  1506.  
  1507. //=====================================================================
  1508.  
  1509. /*
  1510. ===============
  1511. CG_ResetPlayerEntity
  1512.  
  1513. A player just came into view or teleported, so reset all animation info
  1514. ===============
  1515. */
  1516. void CG_ResetPlayerEntity( centity_t *cent ) {
  1517.     cent->errorTime = -99999;        // guarantee no error decay added
  1518.     cent->extrapolated = qfalse;    
  1519.  
  1520.     CG_ClearLerpFrame( &cgs.clientinfo[ cent->currentState.clientNum ], ¢->pe.legs, cent->currentState.legsAnim );
  1521.     CG_ClearLerpFrame( &cgs.clientinfo[ cent->currentState.clientNum ], ¢->pe.torso, cent->currentState.torsoAnim );
  1522.  
  1523.     BG_EvaluateTrajectory( ¢->currentState.pos, cg.time, cent->lerpOrigin );
  1524.     BG_EvaluateTrajectory( ¢->currentState.apos, cg.time, cent->lerpAngles );
  1525.  
  1526.     VectorCopy( cent->lerpOrigin, cent->rawOrigin );
  1527.     VectorCopy( cent->lerpAngles, cent->rawAngles );
  1528.  
  1529.     memset( ¢->pe.legs, 0, sizeof( cent->pe.legs ) );
  1530.     cent->pe.legs.yawAngle = cent->rawAngles[YAW];
  1531.     cent->pe.legs.yawing = qfalse;
  1532.     cent->pe.legs.pitchAngle = 0;
  1533.     cent->pe.legs.pitching = qfalse;
  1534.  
  1535.     memset( ¢->pe.torso, 0, sizeof( cent->pe.legs ) );
  1536.     cent->pe.torso.yawAngle = cent->rawAngles[YAW];
  1537.     cent->pe.torso.yawing = qfalse;
  1538.     cent->pe.torso.pitchAngle = cent->rawAngles[PITCH];
  1539.     cent->pe.torso.pitching = qfalse;
  1540.  
  1541.     if ( cg_debugPosition.integer ) {
  1542.         CG_Printf("%i ResetPlayerEntity yaw=%i\n", cent->currentState.number, cent->pe.torso.yawAngle );
  1543.     }
  1544. }
  1545.  
  1546.